package cn.iocoder.yudao.module.member.assets.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.iocoder.yudao.framework.business.basic.convert.BigDecimalConvert;
import cn.iocoder.yudao.framework.business.basic.dao.query.CoreLambdaQueryWrapper;
import cn.iocoder.yudao.framework.business.basic.dao.repository.LockRedisRepository;
import cn.iocoder.yudao.framework.business.basic.enums.EnumCodable;
import cn.iocoder.yudao.framework.business.basic.service.impl.AbstractService;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.exception.Assertions;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.util.TransactionUtils;
import cn.iocoder.yudao.module.api.infrastructure.config.ConfigApiV2;
import cn.iocoder.yudao.module.api.infrastructure.config.dto.AdConfigRespDTO;
import cn.iocoder.yudao.module.api.infrastructure.config.dto.MemberConfigRespDTO;
import cn.iocoder.yudao.module.api.member.assets.enums.CryptocurrencyDeductionType;
import cn.iocoder.yudao.module.api.member.task.enums.TaskStatusEnum;
import cn.iocoder.yudao.module.api.pay.bill.PayBillApi;
import cn.iocoder.yudao.module.api.pay.bill.dto.PayBillCreateDTO;
import cn.iocoder.yudao.module.api.pay.bill.enums.BillCategoryEnum;
import cn.iocoder.yudao.module.api.pay.bill.enums.BillTypeEnum;
import cn.iocoder.yudao.module.api.pay.wallet.PayWalletApi;
import cn.iocoder.yudao.module.api.pay.wallet.dto.PayWalletAddBalanceReqDTO;
import cn.iocoder.yudao.module.api.pay.wallet.enums.PayWalletBizTypeEnum;
import cn.iocoder.yudao.module.member.assets.bo.CryptocurrencyIncreasingTaskCountBO;
import cn.iocoder.yudao.module.member.assets.dal.dataobject.MemberDigitalAssetsDO;
import cn.iocoder.yudao.module.member.assets.dal.mysql.MemberDigitalAssetsMapper;
import cn.iocoder.yudao.module.member.assets.vo.*;
import cn.iocoder.yudao.module.member.bonus.service.app.AppBonusUserService;
import cn.iocoder.yudao.module.member.vip.service.AppVipUserService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import static cn.iocoder.yudao.module.api.infrastructure.ConfigConstants.CONFIG_AD;
import static cn.iocoder.yudao.module.api.infrastructure.ConfigConstants.CONFIG_MEMBER;

/**
 * 数字资产 Service 实现类
 * // TODO 后续考虑重构改进，将资产变动记录的实现放到本类
 *
 * @author 山野羡民
 */
@Slf4j
@Service
@Validated
public class AppMemberDigitalAssetsServiceImpl extends AbstractService<MemberDigitalAssetsDO, AppMemberDigitalAssetsRespVO> implements AppMemberDigitalAssetsService {
    private static final long TIMEOUT_MILLIS = 10 * DateUtils.SECOND_MILLIS;
    private static final String LOCK_KEY = "xianmin:member:digital-assets:lock:%d";
    private final Lock lock = new ReentrantLock();
    @Resource
    private LockRedisRepository lockRedisRepository;
    @Resource
    private MemberDigitalAssetsMapper memberDigitalAssetsMapper;

    @Resource
    @Lazy // 延迟加载，避免循环依赖
    private AppVipUserService appVipUserService;
    @Resource
    @Lazy // 延迟加载，避免循环依赖
    private AppBonusUserService appBonusUserService;
    @Resource
    @Lazy // 延迟加载，避免循环依赖
    private AppCryptocurrencyIncreasingTaskService appCryptocurrencyIncreasingTaskService;

    @Resource
    private ConfigApiV2 configApiV2;
    @Resource
    private PayBillApi payBillApi;
    @Resource
    private PayWalletApi payWalletApi;

    @Override
    protected MemberDigitalAssetsMapper getInjectedMapper() {
        return memberDigitalAssetsMapper;
    }

    @Override
    public MemberDigitalAssetsDO getOrCreate(Long userId) {
        Assertions.notNull(userId, "未获取到 userId");
        return lockRedisRepository.lock(String.format(LOCK_KEY, userId), TIMEOUT_MILLIS, () -> {
            MemberDigitalAssetsDO entity = getInjectedMapper().selectOne("user_id", userId);
            if (entity == null) {
                entity = new MemberDigitalAssetsDO();
                entity.setUserId(userId);
                entity.setExperience(0);
                entity.setExperienceGet(0);
                entity.setGrowth(0);
                entity.setGrowthGet(0);
                entity.setPoint(0);
                entity.setPointGet(0);
                entity.setRedPacket(0d);
                entity.setRedPacketGet(0d);
                entity.setCryptocurrency(0D);
                entity.setCryptocurrencyOrigin(0D);
                entity.setCryptocurrencyFreezing(0D);
                entity.setCryptocurrencyGet(0D);
                entity.setCredit(0D);
                entity.setCreditGet(0D);
                entity.setShareholding(0D);
                entity.setShareholdingGet(0D);
                entity.setBonus(0D);
                entity.setBonusGet(0D);
                entity.setInterest(0D);
                entity.setInterestGet(0D);
                getInjectedMapper().insert(entity);
            }
            return entity;
        });
    }

    @Override
    public void changeExperience(Long userId, Integer number) {
        int i = NumberUtil.nullToZero(number);
        if (i == 0) {
            log.debug("[changeExperience] 经验为0，不执行改变");
            return;
        }
        MemberDigitalAssetsDO digitalAssets = getOrCreate(userId);
        try {
            lock.lock();
            MemberDigitalAssetsDO entity = new MemberDigitalAssetsDO();
            entity.setId(digitalAssets.getId());
            int experience = digitalAssets.getExperience() + i;
            if (experience < 0) {
                if (i > 0) {
                    // FIXED 2025/08/29 经验值一直累加不减，长年累月下去会导致溢出
                    log.warn("经验值余额溢出：{} {}", digitalAssets.getExperience(), i);
                    experience = Integer.MAX_VALUE;
                } else {
                    log.warn("经验值余额不足：{} {}", digitalAssets.getExperience(), i);
                }
            }
            Assertions.isTrue(experience >= 0, "经验值余额不足"); // 适配传入负数的情况
            entity.setExperience(experience);
            if (i > 0) {
                entity.setExperienceGet(experience == Integer.MAX_VALUE ? Integer.MAX_VALUE : digitalAssets.getExperienceGet() + i);
            }
            getInjectedMapper().updateById(entity);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void changeGrowth(Long userId, Integer number) {
        int i = NumberUtil.nullToZero(number);
        if (i == 0) {
            log.debug("[changeGrowth] 贡献值为0，不执行改变");
            return;
        }
        MemberDigitalAssetsDO digitalAssets = getOrCreate(userId);
        try {
            lock.lock();
            MemberDigitalAssetsDO entity = new MemberDigitalAssetsDO();
            entity.setId(digitalAssets.getId());
            int growth = digitalAssets.getGrowth() + i;
            if (growth < 0) {
                log.warn("贡献值余额不足：{} {}", digitalAssets.getGrowth(), i);
            }
            Assertions.isTrue(growth >= 0, "贡献值余额不足"); // 适配传入负数的情况
            entity.setGrowth(growth);
            if (i > 0) {
                entity.setGrowthGet(digitalAssets.getGrowthGet() + i);
            }
            getInjectedMapper().updateById(entity);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void changePoint(Long userId, Integer number) {
        int i = NumberUtil.nullToZero(number);
        if (i == 0) {
            log.debug("[changePoint] 积分为0，不执行改变");
            return;
        }
        MemberDigitalAssetsDO digitalAssets = getOrCreate(userId);
        try {
            lock.lock();
            MemberDigitalAssetsDO entity = new MemberDigitalAssetsDO();
            entity.setId(digitalAssets.getId());
            int point = digitalAssets.getPoint() + i;
            if (point < 0) {
                log.warn("积分余额不足：{} {}", digitalAssets.getPoint(), i);
            }
            Assertions.isTrue(point >= 0, "积分余额不足"); // 适配传入负数的情况
            entity.setPoint(point);
            if (i > 0) {
                entity.setPointGet(digitalAssets.getPointGet() + i);
            }
            getInjectedMapper().updateById(entity);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void changeRevenue(Long userId, Double number) {
        double i = NumberUtil.nullToZero(number);
        if (i == 0) {
            log.debug("[changeRevenue] 营业收益为0，不执行改变");
            return;
        }
        MemberDigitalAssetsDO digitalAssets = getOrCreate(userId);
        try {
            lock.lock();
            MemberDigitalAssetsDO entity = new MemberDigitalAssetsDO();
            entity.setId(digitalAssets.getId());
            double revenue = digitalAssets.getRevenue() + i;
            if (revenue < 0) {
                log.warn("营业收益余额不足：{} {}", digitalAssets.getRevenue(), i);
            }
            Assertions.isTrue(revenue >= 0, "营业收益余额不足"); // 适配传入负数的情况
            entity.setRevenue(revenue);
            if (i > 0) {
                entity.setRevenueGet(digitalAssets.getRevenueGet() + i);
            }
            getInjectedMapper().updateById(entity);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void changeCommission(Long userId, Double number) {
        double i = NumberUtil.nullToZero(number);
        if (i == 0) {
            log.debug("[changeCommission] 佣金为0，不执行改变");
            return;
        }
        MemberDigitalAssetsDO digitalAssets = getOrCreate(userId);
        try {
            lock.lock();
            MemberDigitalAssetsDO entity = new MemberDigitalAssetsDO();
            entity.setId(digitalAssets.getId());
            double commission = digitalAssets.getCommission() + i;
            if (commission < 0) {
                log.warn("佣金余额不足：{} {}", digitalAssets.getCommission(), i);
            }
            Assertions.isTrue(commission >= 0, "佣金余额不足"); // 适配传入负数的情况
            entity.setCommission(commission);
            if (i > 0) {
                entity.setCommissionGet(digitalAssets.getCommissionGet() + i);
            }
            getInjectedMapper().updateById(entity);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void changeRedPacket(Long userId, Double number) {
        double i = NumberUtil.nullToZero(number);
        if (i == 0) {
            log.debug("[changeRedPacket] 红包为0，不执行改变");
            return;
        }
        MemberDigitalAssetsDO digitalAssets = getOrCreate(userId);
        try {
            lock.lock();
            MemberDigitalAssetsDO entity = new MemberDigitalAssetsDO();
            entity.setId(digitalAssets.getId());
            double redPacket = digitalAssets.getRedPacket() + i;
            if (redPacket < 0) {
                log.warn("红包余额不足：{} {}", digitalAssets.getRedPacket(), i);
            }
            Assertions.isTrue(redPacket >= 0, "红包余额不足"); // 适配传入负数的情况
            entity.setRedPacket(redPacket);
            if (i > 0) {
                entity.setRedPacketGet(digitalAssets.getRedPacketGet() + i);
            }
            getInjectedMapper().updateById(entity);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void changeBonus(Long userId, Double number) {
        double i = NumberUtil.nullToZero(number);
        if (i == 0) {
            log.debug("[changeBonus] 分红为0，不执行改变");
            return;
        }
        MemberDigitalAssetsDO digitalAssets = getOrCreate(userId);
        try {
            lock.lock();
            MemberDigitalAssetsDO entity = new MemberDigitalAssetsDO();
            entity.setId(digitalAssets.getId());
            double bonus = digitalAssets.getBonus() + i;
            if (bonus < 0) {
                log.warn("分红余额不足：{} {}", digitalAssets.getBonus(), i);
            }
            Assertions.isTrue(bonus >= 0, "分红余额不足"); // 适配传入负数的情况
            entity.setBonus(bonus);
            if (i > 0) {
                entity.setBonusGet(digitalAssets.getBonusGet() + i);
            }
            getInjectedMapper().updateById(entity);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void changeInterest(Long userId, Double number) {
        double i = NumberUtil.nullToZero(number);
        if (i == 0) {
            log.debug("[changeInterest] 消费金为0，不执行改变");
            return;
        }
        MemberDigitalAssetsDO digitalAssets = getOrCreate(userId);
        try {
            lock.lock();
            MemberDigitalAssetsDO entity = new MemberDigitalAssetsDO();
            entity.setId(digitalAssets.getId());
            double interest = digitalAssets.getInterest() + i;
            if (interest < 0) {
                log.warn("消费金余额不足：{} {}", digitalAssets.getInterest(), i);
            }
            Assertions.isTrue(interest >= 0, "消费金余额不足"); // 适配传入负数的情况
            entity.setInterest(interest);
            if (i > 0) {
                entity.setInterestGet(digitalAssets.getInterestGet() + i);
            }
            getInjectedMapper().updateById(entity);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public CryptocurrencyDeductionType payCryptocurrencyIncrement(Long userId, Double number) {
        double i = NumberUtil.nullToZero(number);
        if (i <= 0) {
            log.debug("[payCryptocurrencyIncrement] 联盟豆或收益豆数量无效，不执行改变");
            return CryptocurrencyDeductionType.NONE;
        }
        CryptocurrencyDeductionType deductionType;
        MemberDigitalAssetsDO digitalAssets = getOrCreate(userId);
        try {
            lock.lock();
            MemberDigitalAssetsDO entity = new MemberDigitalAssetsDO();
            entity.setId(digitalAssets.getId());
            if (digitalAssets.getCryptocurrencyIncrement() >= i) {
                // 若收益豆充足，则优先使用收益豆抵扣
                entity.setCryptocurrency(digitalAssets.getCryptocurrency() - i);
                deductionType = CryptocurrencyDeductionType.INCREMENT;
            } else {
                // 若收益豆不足，则联盟豆自动转为收益豆抵扣
                if (digitalAssets.getCryptocurrencyOrigin() < i) {
                    log.warn("[payCryptocurrencyIncrement] 联盟豆余额不足：{} {}", digitalAssets.getCryptocurrencyOrigin(), i);
                }
                Assertions.isTrue(digitalAssets.getCryptocurrencyOrigin() >= i, "联盟豆余额不足");
                entity.setCryptocurrency(digitalAssets.getCryptocurrency() - i);
                entity.setCryptocurrencyOrigin(digitalAssets.getCryptocurrencyOrigin() - i);
                deductionType = CryptocurrencyDeductionType.ORIGIN;
            }
            getInjectedMapper().updateById(entity);
            return deductionType;
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void refundCryptocurrencyIncrement(Long userId, Double number) {
        double i = NumberUtil.nullToZero(number);
        if (i <= 0) {
            log.debug("[refundCryptocurrencyIncrement] 收益豆数量无效，不执行改变");
            return;
        }
        MemberDigitalAssetsDO digitalAssets = getOrCreate(userId);
        try {
            lock.lock();
            MemberDigitalAssetsDO entity = new MemberDigitalAssetsDO();
            entity.setId(digitalAssets.getId());
            entity.setCryptocurrency(digitalAssets.getCryptocurrency() + i);
            getInjectedMapper().updateById(entity);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void exchangeCryptocurrencyOriginToIncrement(Long userId, Double number) {
        double i = NumberUtil.nullToZero(number);
        if (i <= 0) {
            log.debug("[exchangeCryptocurrencyOriginToIncrement] 联盟豆数量无效，不执行改变");
            return;
        }
        MemberDigitalAssetsDO digitalAssets = getOrCreate(userId);
        if (digitalAssets.getCryptocurrencyOrigin() < i) {
            log.warn("[exchangeCryptocurrencyOriginToIncrement] 联盟豆余额不足：{} {}", digitalAssets.getCryptocurrencyOrigin(), i);
        }
        Assertions.isTrue(digitalAssets.getCryptocurrencyOrigin() >= i, "联盟豆余额不足");
        try {
            lock.lock();
            MemberDigitalAssetsDO entity = new MemberDigitalAssetsDO();
            entity.setId(digitalAssets.getId());
            entity.setCryptocurrencyOrigin(digitalAssets.getCryptocurrencyOrigin() - i);
            getInjectedMapper().updateById(entity);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void changeCryptocurrencyOrigin(Long userId, Double number) {
        double i = NumberUtil.nullToZero(number);
        if (i == 0) {
            log.debug("[changeCryptocurrencyOrigin] 联盟豆为0，不执行改变");
            return;
        }
        MemberDigitalAssetsDO digitalAssets = getOrCreate(userId);
        try {
            lock.lock();
            MemberDigitalAssetsDO entity = new MemberDigitalAssetsDO();
            entity.setId(digitalAssets.getId());
            double cryptocurrencyOrigin = digitalAssets.getCryptocurrencyOrigin() + i;
            if (cryptocurrencyOrigin < 0) {
                log.warn("[changeCryptocurrencyOrigin] 联盟豆余额不足：{} {}", digitalAssets.getCryptocurrencyOrigin(), i);
            }
            Assertions.isTrue(cryptocurrencyOrigin >= 0, "联盟豆余额不足"); // 适配传入负数的情况
            entity.setCryptocurrencyOrigin(cryptocurrencyOrigin);
            double cryptocurrency = digitalAssets.getCryptocurrency() + i;
            if (cryptocurrency < 0) {
                log.warn("[changeCryptocurrencyOrigin] 联盟豆（含收益豆）余额不足：{} {}", digitalAssets.getCryptocurrency(), i);
            }
            Assertions.isTrue(cryptocurrency >= 0, "联盟豆（含收益豆）余额不足"); // 适配传入负数的情况
            entity.setCryptocurrency(cryptocurrency);
            if (i > 0) {
                entity.setCryptocurrencyGet(digitalAssets.getCryptocurrencyGet() + i);
            }
            getInjectedMapper().updateById(entity);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void exchangeCryptocurrencyIncrementToMoney(Long userId, Double number) {
        double i = NumberUtil.nullToZero(number);
        if (i <= 0) {
            log.debug("[exchangeCryptocurrencyIncrementToMoney] 收益豆数量无效，不执行改变");
            return;
        }
        MemberDigitalAssetsDO digitalAssets = getOrCreate(userId);
        try {
            lock.lock();
            MemberDigitalAssetsDO entity = new MemberDigitalAssetsDO();
            entity.setId(digitalAssets.getId());
            if (digitalAssets.getCryptocurrencyIncrement() < i) {
                log.warn("收益豆余额不足：{} {}", digitalAssets.getCryptocurrencyIncrement(), i);
            }
            Assertions.isTrue(digitalAssets.getCryptocurrencyIncrement() >= i, "收益豆余额不足"); // 适配传入负数的情况
            entity.setCryptocurrency(digitalAssets.getCryptocurrency() - i);
            getInjectedMapper().updateById(entity);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void unfreezeCryptocurrencyOrigin(Long userId, Double number) {
        double i = NumberUtil.nullToZero(number);
        if (i <= 0) {
            log.debug("[unfreezeCryptocurrencyOrigin] 联盟豆数量为无效，不执行改变");
            return;
        }
        MemberDigitalAssetsDO digitalAssets = getOrCreate(userId);
        try {
            lock.lock();
            MemberDigitalAssetsDO entity = new MemberDigitalAssetsDO();
            entity.setId(digitalAssets.getId());
            double cryptocurrencyFreezing = digitalAssets.getCryptocurrencyFreezing() - i;
            if (cryptocurrencyFreezing < 0) {
                log.warn("[unfreezeCryptocurrencyOrigin] 待释放的联盟豆余额不足：{} {}", digitalAssets.getCryptocurrencyFreezing(), i);
            }
            Assertions.isTrue(cryptocurrencyFreezing >= 0, "待释放的联盟豆余额不足");
            entity.setCryptocurrencyFreezing(cryptocurrencyFreezing);
            entity.setCryptocurrencyOrigin(digitalAssets.getCryptocurrencyOrigin() + i);
            entity.setCryptocurrency(digitalAssets.getCryptocurrency() + i);
            entity.setCryptocurrencyGet(digitalAssets.getCryptocurrencyGet() + i);
            getInjectedMapper().updateById(entity);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void changeCryptocurrencyFreezing(Long userId, Double number) {
        double i = NumberUtil.nullToZero(number);
        if (i == 0) {
            log.debug("[changeCryptocurrencyFreezing] 联盟豆为0，不执行改变");
            return;
        }
        MemberDigitalAssetsDO digitalAssets = getOrCreate(userId);
        try {
            lock.lock();
            MemberDigitalAssetsDO entity = new MemberDigitalAssetsDO();
            entity.setId(digitalAssets.getId());
            double cryptocurrencyFreezing = digitalAssets.getCryptocurrencyFreezing() + i;
            if (cryptocurrencyFreezing < 0) {
                log.warn("[changeCryptocurrencyFreezing] 待释放的联盟豆余额不足：{} {}", digitalAssets.getCryptocurrencyFreezing(), i);
            }
            Assertions.isTrue(cryptocurrencyFreezing >= 0, "待释放的联盟豆余额不足"); // 适配传入负数的情况
            entity.setCryptocurrencyFreezing(cryptocurrencyFreezing);
            getInjectedMapper().updateById(entity);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void exchangeCryptocurrencyOriginToIncreasing(Long userId, Double number) {
        double i = NumberUtil.nullToZero(number);
        if (i <= 0) {
            log.debug("[exchangeCryptocurrencyOriginToIncreasing] 增值中的联盟豆为0，不执行改变");
            return;
        }
        MemberDigitalAssetsDO digitalAssets = getOrCreate(userId);
        try {
            lock.lock();
            MemberDigitalAssetsDO entity = new MemberDigitalAssetsDO();
            entity.setId(digitalAssets.getId());
            Assertions.isTrue(digitalAssets.getCryptocurrencyOrigin() >= i, "联盟豆不够，无法投入");
            entity.setCryptocurrency(digitalAssets.getCryptocurrency() - i);
            entity.setCryptocurrencyOrigin(digitalAssets.getCryptocurrencyOrigin() - i);
            entity.setCryptocurrencyIncreasing(digitalAssets.getCryptocurrencyIncreasing() + i);
            getInjectedMapper().updateById(entity);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void increaseCryptocurrencyIncrement(Long userId, Double number) {
        double i = NumberUtil.nullToZero(number);
        if (i <= 0) {
            log.debug("[increaseCryptocurrencyIncrement] 增值的联盟豆无效，不执行改变");
            return;
        }
        MemberDigitalAssetsDO digitalAssets = getOrCreate(userId);
        try {
            lock.lock();
            MemberDigitalAssetsDO entity = new MemberDigitalAssetsDO();
            entity.setId(digitalAssets.getId());
            entity.setCryptocurrency(digitalAssets.getCryptocurrency() + i);
            entity.setCryptocurrencyGet(digitalAssets.getCryptocurrencyGet() + i);
            getInjectedMapper().updateById(entity);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void returnCryptocurrencyIncreasing(Long userId, Double number) {
        double i = NumberUtil.nullToZero(number);
        if (i <= 0) {
            log.debug("[returnCryptocurrencyIncreasing] 返还的联盟豆本金无效，不执行改变");
            return;
        }
        MemberDigitalAssetsDO digitalAssets = getOrCreate(userId);
        try {
            lock.lock();
            MemberDigitalAssetsDO entity = new MemberDigitalAssetsDO();
            entity.setId(digitalAssets.getId());
            entity.setCryptocurrencyIncreasing(digitalAssets.getCryptocurrencyIncreasing() - i);
            entity.setCryptocurrency(digitalAssets.getCryptocurrency() + i);
            entity.setCryptocurrencyGet(digitalAssets.getCryptocurrencyGet() + i);
            getInjectedMapper().updateById(entity);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public List<AppMemberDigitalAssetsRespVO> getListByUserIds(Collection<Long> userIds) {
        if (CollUtil.isEmpty(userIds)) {
            return CollUtil.newArrayList();
        }
        CoreLambdaQueryWrapper<MemberDigitalAssetsDO> wrapper = new CoreLambdaQueryWrapper<>();
        wrapper.in(MemberDigitalAssetsDO::getUserId, userIds);
        return getList(wrapper);
    }

    @Override
    public AppCryptocurrencyOverviewRespVO getCryptocurrencyOverview(Long userId) {
        AppCryptocurrencyOverviewRespVO respVO = new AppCryptocurrencyOverviewRespVO();
        MemberDigitalAssetsDO memberDigitalAssetsDO = getOrCreate(userId);
        respVO.setCryptocurrency(BigDecimalConvert.INSTANCE.convert(memberDigitalAssetsDO.getCryptocurrency()));
        respVO.setCryptocurrencyOrigin(BigDecimalConvert.INSTANCE.convert(memberDigitalAssetsDO.getCryptocurrencyOrigin()));
        respVO.setCryptocurrencyIncrement(BigDecimalConvert.INSTANCE.convert(memberDigitalAssetsDO.getCryptocurrencyIncrement()));
        respVO.setCryptocurrencyIncreasing(BigDecimalConvert.INSTANCE.convert(memberDigitalAssetsDO.getCryptocurrencyIncreasing()));
        respVO.setCryptocurrencyFreezing(BigDecimalConvert.INSTANCE.convert(memberDigitalAssetsDO.getCryptocurrencyFreezing()));
        MemberConfigRespDTO memberConfigRespDTO = configApiV2.getConfig(CONFIG_MEMBER, MemberConfigRespDTO.class);
        respVO.setCryptocurrencyToMoneyTips(memberConfigRespDTO.getCryptocurrencyToMoneyTips());
        respVO.setCryptocurrencyToMoneyPercent(memberConfigRespDTO.getCryptocurrencyToMoneyPercent());
        respVO.setCryptocurrencyNotIncrementTips(memberConfigRespDTO.getCryptocurrencyNotIncrementTips());
        respVO.setCryptocurrencyIncrementTips(memberConfigRespDTO.getCryptocurrencyIncrementTips());
        respVO.setCryptocurrencyIncrementPresetAmounts(memberConfigRespDTO.getCryptocurrencyIncrementPresetAmounts());
        respVO.setCryptocurrencyIncrementPercent(memberConfigRespDTO.getCryptocurrencyIncrementPercent());
        respVO.setCryptocurrencyIncrementDays(memberConfigRespDTO.getCryptocurrencyIncrementDays());
        respVO.setBizTypes(EnumCodable.toVos(BillTypeEnum.CRYPTOCURRENCY_DEDUCTION,
                BillTypeEnum.EXCHANGE_MONEY, BillTypeEnum.TRANSFER_INPUT,
                BillTypeEnum.TRANSFER_OUTPUT
        ));
        return respVO;
    }

    @Override
    public void submitCryptocurrencyOriginToMoney(Long userId, Integer amount) {
        Assertions.notNull(userId, "用户编号不能为空");
        Assertions.isTrue(amount > 0, "输入的数量无效"); // 防止恶意传入负数
        MemberDigitalAssetsDO memberDigitalAssetsDO = getOrCreate(userId);
        Assertions.isTrue(memberDigitalAssetsDO.getCryptocurrencyOrigin() >= amount, "联盟豆余额不足");
        MemberConfigRespDTO memberConfigRespDTO = configApiV2.getConfig(CONFIG_MEMBER, MemberConfigRespDTO.class);
        int cryptocurrencyToMoney = (int) (NumberUtil.nullToZero(amount) * memberConfigRespDTO.getCryptocurrencyToMoneyPercent());
        Assertions.isTrue(cryptocurrencyToMoney > 0, "联盟豆数量过少，无法兑换成零钱");
        TransactionUtils.transactional(() -> {
            // 扣减用户的联盟豆
            changeCryptocurrencyOrigin(memberDigitalAssetsDO.getUserId(), -1d * amount);
            // 记录账单
            payBillApi.expendAsync(new PayBillCreateDTO()
                    .setUserId(memberDigitalAssetsDO.getUserId())
                    .setCategory(BillCategoryEnum.CRYPTOCURRENCY_ORIGIN)
                    .setType(BillTypeEnum.EXCHANGE_MONEY)
                    .setNumber(-1d * cryptocurrencyToMoney)
                    .setMark("转出 - 兑换零钱")
                    .setRelateId("")
                    .setExtendField(""));
            // 增加用户的零钱
            PayWalletAddBalanceReqDTO balanceReqDTO = new PayWalletAddBalanceReqDTO();
            balanceReqDTO.setUserId(memberDigitalAssetsDO.getUserId());
            balanceReqDTO.setUserType(UserTypeEnum.MEMBER.getValue());
            balanceReqDTO.setBizType(PayWalletBizTypeEnum.CRYPTOCURRENCY_ORIGIN_TO_MONEY.getValue());
            balanceReqDTO.setBizId(String.valueOf(memberDigitalAssetsDO.getId()));
            balanceReqDTO.setPrice(cryptocurrencyToMoney);
            payWalletApi.addWalletBalance(balanceReqDTO);
            return null;
        });
    }

    @Override
    public void submitCryptocurrencyIncrementToMoney(Long userId, Integer amount) {
        Assertions.notNull(userId, "用户编号不能为空");
        Assertions.isTrue(amount > 0, "输入的数量无效"); // 防止恶意传入负数
        MemberDigitalAssetsDO memberDigitalAssetsDO = getOrCreate(userId);
        Assertions.isTrue(memberDigitalAssetsDO.getCryptocurrencyIncrement() >= amount, "收益豆余额不足");
        MemberConfigRespDTO memberConfigRespDTO = configApiV2.getConfig(CONFIG_MEMBER, MemberConfigRespDTO.class);
        int cryptocurrencyToMoney = (int) (NumberUtil.nullToZero(amount) * memberConfigRespDTO.getCryptocurrencyToMoneyPercent());
        Assertions.isTrue(cryptocurrencyToMoney > 0, "收益豆数量过少，无法兑换零钱");
        TransactionUtils.transactional(() -> {
            // 扣减用户的联盟豆（含收益豆）
            exchangeCryptocurrencyIncrementToMoney(memberDigitalAssetsDO.getUserId(), 1d * amount);
            // 记录账单
            payBillApi.expendAsync(new PayBillCreateDTO()
                    .setUserId(memberDigitalAssetsDO.getUserId())
                    .setCategory(BillCategoryEnum.CRYPTOCURRENCY_INCREMENT)
                    .setType(BillTypeEnum.EXCHANGE_MONEY)
                    .setNumber(-1d * amount)
                    .setMark("转出 - 兑换零钱")
                    .setRelateId("")
                    .setExtendField(""));
            // 增加用户的零钱
            PayWalletAddBalanceReqDTO balanceReqDTO = new PayWalletAddBalanceReqDTO();
            balanceReqDTO.setUserId(memberDigitalAssetsDO.getUserId());
            balanceReqDTO.setUserType(UserTypeEnum.MEMBER.getValue());
            balanceReqDTO.setBizType(PayWalletBizTypeEnum.CRYPTOCURRENCY_INCREMENT_TO_MONEY.getValue());
            balanceReqDTO.setBizId(String.valueOf(memberDigitalAssetsDO.getId()));
            balanceReqDTO.setPrice(cryptocurrencyToMoney);
            payWalletApi.addWalletBalance(balanceReqDTO);
            return null;
        });
    }

    @Override
    public void submitCryptocurrencyOriginToIncrement(Long userId, Integer amount) {
        Assertions.notNull(userId, "用户编号不能为空");
        Assertions.isTrue(amount > 0, "输入的数量无效"); // 防止恶意传入负数
        MemberDigitalAssetsDO memberDigitalAssetsDO = getOrCreate(userId);
        Assertions.isTrue(memberDigitalAssetsDO.getCryptocurrencyOrigin() >= amount, "联盟豆余额不足");
        TransactionUtils.transactional(() -> {
            // 扣减可投入增值的联盟豆、增加已增值的联盟豆
            exchangeCryptocurrencyOriginToIncrement(memberDigitalAssetsDO.getUserId(), 1d * amount);
            // 记录账单
            payBillApi.expendAsync(new PayBillCreateDTO()
                    .setUserId(memberDigitalAssetsDO.getUserId())
                    .setCategory(BillCategoryEnum.CRYPTOCURRENCY_ORIGIN)
                    .setType(BillTypeEnum.TRANSFER_OUTPUT)
                    .setNumber(-1d * amount)
                    .setMark("转出 - 兑换为收益豆")
                    .setRelateId("")
                    .setExtendField(""));
            // 记录账单
            payBillApi.incomeAsync(new PayBillCreateDTO()
                    .setUserId(memberDigitalAssetsDO.getUserId())
                    .setCategory(BillCategoryEnum.CRYPTOCURRENCY_INCREMENT)
                    .setType(BillTypeEnum.TRANSFER_INPUT)
                    .setNumber(1d * amount)
                    .setMark("转入 - 兑换自联盟豆")
                    .setRelateId("")
                    .setExtendField(""));
            return null;
        });
    }

    @Override
    public void submitCryptocurrencyOriginToIncreasing(Long userId, Integer amount) {
        Assertions.notNull(userId, "用户编号不能为空");
        Assertions.isTrue(amount > 0, "输入的数量无效"); // 防止恶意传入负数
        Assertions.isTrue(appVipUserService.isValid(userId), "还不是会员或会员已过期，不能兑换投入");
        Assertions.isTrue(amount % 10 == 0, "联盟豆必须是 10 的整数倍"); // 限定 10 的整数倍是为了方便每日增值计算取整
        MemberDigitalAssetsDO memberDigitalAssetsDO = getOrCreate(userId);
        Assertions.isTrue(memberDigitalAssetsDO.getCryptocurrencyOrigin() >= amount, "联盟豆余额不足");
        TransactionUtils.transactional(() -> {
            // 增加增值中的联盟豆
            exchangeCryptocurrencyOriginToIncreasing(memberDigitalAssetsDO.getUserId(), 1d * amount);
            // 记录账单
            payBillApi.expendAsync(new PayBillCreateDTO()
                    .setUserId(memberDigitalAssetsDO.getUserId())
                    .setCategory(BillCategoryEnum.CRYPTOCURRENCY_ORIGIN)
                    .setType(BillTypeEnum.TRANSFER_OUTPUT)
                    .setNumber(-1d * amount)
                    .setMark("转出 - 兑换投入收益池")
                    .setRelateId("")
                    .setExtendField(""));
            // 创建联盟豆增值任务
            MemberConfigRespDTO memberConfigRespDTO = configApiV2.getConfig(CONFIG_MEMBER, MemberConfigRespDTO.class);
            AdConfigRespDTO adConfigRespDTO = configApiV2.getConfig(CONFIG_AD, AdConfigRespDTO.class);
            appCryptocurrencyIncreasingTaskService.create(amount, memberConfigRespDTO.getCryptocurrencyIncrementPercent(), memberConfigRespDTO.getCryptocurrencyIncrementDays(), adConfigRespDTO.getDailyTaskAdCount(), memberDigitalAssetsDO.getUserId());
            return null;
        });
    }

    @Override
    public AppBonusOverviewRespVO getBonusOverview(Long userId) {
        AppBonusOverviewRespVO respVO = new AppBonusOverviewRespVO();
        MemberDigitalAssetsDO memberDigitalAssetsDO = getOrCreate(userId);
        respVO.setBonus(BigDecimalConvert.INSTANCE.convert(memberDigitalAssetsDO.getBonus()));
        MemberConfigRespDTO memberConfigRespDTO = configApiV2.getConfig(CONFIG_MEMBER, MemberConfigRespDTO.class);
        respVO.setBonusToMoneyTips(memberConfigRespDTO.getBonusToMoneyTips());
        respVO.setBonusToMoneyPercent(memberConfigRespDTO.getBonusToMoneyPercent());
        respVO.setBizTypes(EnumCodable.toVos(BillTypeEnum.EXCHANGE_PRODUCT, BillTypeEnum.EXCHANGE_MONEY));
        return respVO;
    }

    @Override
    public void submitBonusToMoney(Long userId, Integer amount) {
        Assertions.notNull(userId, "用户编号不能为空");
        Assertions.isTrue(amount > 0, "输入的数量无效"); // 防止恶意传入负数
        MemberDigitalAssetsDO memberDigitalAssetsDO = getOrCreate(userId);
        Assertions.isTrue(memberDigitalAssetsDO.getBonus() >= amount, "分红余额不足");
        MemberConfigRespDTO memberConfigRespDTO = configApiV2.getConfig(CONFIG_MEMBER, MemberConfigRespDTO.class);
        int money = (int) (NumberUtil.nullToZero(amount) * memberConfigRespDTO.getBonusToMoneyPercent());
        Assertions.isTrue(money > 0, "分红数量过少，无法兑换零钱");
        TransactionUtils.transactional(() -> {
            // 扣减用户的分红
            changeBonus(memberDigitalAssetsDO.getUserId(), -1d * amount);
            // 记录账单
            payBillApi.expendAsync(new PayBillCreateDTO()
                    .setUserId(memberDigitalAssetsDO.getUserId())
                    .setCategory(BillCategoryEnum.BONUS)
                    .setType(BillTypeEnum.EXCHANGE_MONEY)
                    .setNumber(-1d * amount)
                    .setMark("转出 - 兑换零钱")
                    .setRelateId("")
                    .setExtendField(""));
            // 增加用户的零钱
            PayWalletAddBalanceReqDTO balanceReqDTO = new PayWalletAddBalanceReqDTO();
            balanceReqDTO.setUserId(memberDigitalAssetsDO.getUserId());
            balanceReqDTO.setUserType(UserTypeEnum.MEMBER.getValue());
            balanceReqDTO.setBizType(PayWalletBizTypeEnum.BONUS_TO_MONEY.getValue());
            balanceReqDTO.setBizId(String.valueOf(memberDigitalAssetsDO.getId()));
            balanceReqDTO.setPrice(money);
            payWalletApi.addWalletBalance(balanceReqDTO);
            return null;
        });
    }

    @Override
    public AppShareholdingOverviewRespVO getShareholdingOverview(Long userId) {
        AppShareholdingOverviewRespVO respVO = new AppShareholdingOverviewRespVO();
        MemberDigitalAssetsDO memberDigitalAssetsDO = getOrCreate(userId);
        respVO.setShareholding(BigDecimalConvert.INSTANCE.convert(memberDigitalAssetsDO.getShareholding()));
        respVO.setShareholdingGet(BigDecimalConvert.INSTANCE.convert(memberDigitalAssetsDO.getShareholdingGet()));
        respVO.setInterest(BigDecimalConvert.INSTANCE.convert(memberDigitalAssetsDO.getInterest()));
        respVO.setInterestGet(BigDecimalConvert.INSTANCE.convert(memberDigitalAssetsDO.getInterestGet()));
        MemberConfigRespDTO memberConfigRespDTO = configApiV2.getConfig(CONFIG_MEMBER, MemberConfigRespDTO.class);
        respVO.setShareholdingToInterestTips(memberConfigRespDTO.getShareholdingToInterestTips());
        respVO.setShareholdingToInterestPercent(memberConfigRespDTO.getShareholdingToInterestPercent());
        respVO.setInterestToMoneyTips(memberConfigRespDTO.getInterestToMoneyTips());
        respVO.setInterestToMoneyPercent(memberConfigRespDTO.getInterestToMoneyPercent());
        respVO.setBizTypes(EnumCodable.toVos(BillTypeEnum.EXCHANGE_PRODUCT, BillTypeEnum.EXCHANGE_MONEY));
        return respVO;
    }

    @Override
    public void submitInterestToMoney(Long userId, Integer amount) {
        Assertions.notNull(userId, "用户编号不能为空");
        Assertions.isTrue(amount > 0, "输入的数量无效"); // 防止恶意传入负数
        MemberDigitalAssetsDO memberDigitalAssetsDO = getOrCreate(userId);
        Assertions.isTrue(memberDigitalAssetsDO.getBonus() >= amount, "消费金余额不足");
        MemberConfigRespDTO memberConfigRespDTO = configApiV2.getConfig(CONFIG_MEMBER, MemberConfigRespDTO.class);
        int money = (int) (NumberUtil.nullToZero(amount) * memberConfigRespDTO.getInterestToMoneyPercent());
        Assertions.isTrue(money > 0, "消费金数量过少，无法兑换零钱");
        TransactionUtils.transactional(() -> {
            // 扣减用户的消费金
            changeInterest(memberDigitalAssetsDO.getUserId(), -1d * amount);
            // 记录账单
            payBillApi.expendAsync(new PayBillCreateDTO()
                    .setUserId(memberDigitalAssetsDO.getUserId())
                    .setCategory(BillCategoryEnum.INTEREST)
                    .setType(BillTypeEnum.EXCHANGE_MONEY)
                    .setNumber(-1d * amount)
                    .setMark("转出 - 兑换零钱")
                    .setRelateId("")
                    .setExtendField(""));
            // 增加用户的零钱
            PayWalletAddBalanceReqDTO balanceReqDTO = new PayWalletAddBalanceReqDTO();
            balanceReqDTO.setUserId(memberDigitalAssetsDO.getUserId());
            balanceReqDTO.setUserType(UserTypeEnum.MEMBER.getValue());
            balanceReqDTO.setBizType(PayWalletBizTypeEnum.INTEREST_TO_MONEY.getValue());
            balanceReqDTO.setBizId(String.valueOf(memberDigitalAssetsDO.getId()));
            balanceReqDTO.setPrice(money);
            payWalletApi.addWalletBalance(balanceReqDTO);
            return null;
        });
    }

    @Override
    public List<AppCryptocurrencyIncreasingPresetVO> getCryptocurrencyIncreasingPresetList(Long userId) {
        MemberConfigRespDTO memberConfigRespDTO = configApiV2.getConfig(CONFIG_MEMBER, MemberConfigRespDTO.class);
        List<CryptocurrencyIncreasingTaskCountBO> countBOList = appCryptocurrencyIncreasingTaskService.count(userId, TaskStatusEnum.DOING);
        List<AppCryptocurrencyIncreasingPresetVO> list = new ArrayList<>();
        for (Integer presetAmount : memberConfigRespDTO.getCryptocurrencyIncrementPresetAmounts()) {
            AppCryptocurrencyIncreasingPresetVO cryptocurrencyIncreasingPresetVO = new AppCryptocurrencyIncreasingPresetVO();
            cryptocurrencyIncreasingPresetVO.setInAmount(presetAmount);
            cryptocurrencyIncreasingPresetVO.setOutAmount(presetAmount + (int) (presetAmount * memberConfigRespDTO.getCryptocurrencyIncrementPercent() / 100));
            cryptocurrencyIncreasingPresetVO.setRequireDays(memberConfigRespDTO.getCryptocurrencyIncrementDays());
            int doingCount = 0;
            if (CollUtil.isNotEmpty(countBOList)) {
                for (CryptocurrencyIncreasingTaskCountBO countBO : countBOList) {
                    if (Objects.equals(countBO.getInAmount(), presetAmount)) {
                        doingCount += countBO.getCount();
                        break;
                    }
                }
            }
            cryptocurrencyIncreasingPresetVO.setDoingCount(doingCount);
            list.add(cryptocurrencyIncreasingPresetVO);
        }
        return list;
    }

    @Override
    public AppGrowthOverviewRespVO getGrowthOverview(Long userId) {
        AppGrowthOverviewRespVO respVO = new AppGrowthOverviewRespVO();
        MemberDigitalAssetsDO memberDigitalAssetsDO = getOrCreate(userId);
        respVO.setGrowth(BigDecimalConvert.INSTANCE.convert(memberDigitalAssetsDO.getGrowth()));
        respVO.setGrowthGet(BigDecimalConvert.INSTANCE.convert(memberDigitalAssetsDO.getGrowthGet()));
        respVO.setBonus(BigDecimalConvert.INSTANCE.convert(memberDigitalAssetsDO.getBonus()));
        respVO.setBonusGet(BigDecimalConvert.INSTANCE.convert(memberDigitalAssetsDO.getBonusGet()));
        MemberConfigRespDTO memberConfigRespDTO = configApiV2.getConfig(CONFIG_MEMBER, MemberConfigRespDTO.class);
        respVO.setBonusToMoneyTips(memberConfigRespDTO.getBonusToMoneyTips());
        respVO.setBonusToMoneyPercent(memberConfigRespDTO.getBonusToMoneyPercent());
        respVO.setBonusBenefitOwned(appBonusUserService.isValid(userId));
        respVO.setBonusBenefitDays(memberConfigRespDTO.getBonusBenefitDays());
        respVO.setBonusBenefitPrice(memberConfigRespDTO.getBonusBenefitPrice());
        respVO.setBonusBenefitTips(memberConfigRespDTO.getBonusBenefitTips());
        return respVO;
    }

}